package com.wuyan.web.base.config;

import com.wuyan.web.base.helper.rep.RepCodeEnum;
import com.wuyan.web.base.helper.rep.RepHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 全局异常捕捉处理类
 */

@Slf4j
@ControllerAdvice
public class ExceptionConfig implements RepHelper {

    /**
     * 统一异常处理，Json返回的
     *
     * @param request   请求实体
     * @param exception 异常
     * @return 返回JSON字符串或者error页面
     */
    @ResponseBody
    @ExceptionHandler(value = Throwable.class)
    public Object defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Throwable exception) {
        log.error(exception.getMessage(), exception);

        // 异常堆信息，主要是日志跟踪
        String errorMsg = exception.getMessage();
        // 请求的URL
        String reqUrl = request.getRequestURL().toString();

        // 前台传递过来的参数
        Enumeration<String> ent = request.getParameterNames();
        List<String> sbParaList = new ArrayList<>();
        while (ent.hasMoreElements()) {
            String paraName = ent.nextElement();
            String paraVal = request.getParameter(paraName);
            sbParaList.add(paraName + "=" + paraVal);
        }

        // 请求的参数，多个以逗号隔开
        String reqParas = "该请求没有参数";
        if (sbParaList.size() > 0) {
            reqParas = StringUtils.join(sbParaList.toArray(), ",");
        }

        String msg = "{url: '" + reqUrl + "', param: '" + reqParas + "', method: '" + request.getMethod() + "'}";

        // 请求类型判断
        String accept = request.getHeader("accept");
        if (accept != null && !accept.contains("text/html")) {
            return getErrMsg(response, exception, errorMsg, msg);
        } else {
            ModelAndView mav = new ModelAndView();
            mav.setViewName("error");
            return mav;
        }
    }

    /**
     * 根据情况给出不同的错误响应
     *
     * @param response 响应体
     * @param e        错误信息
     * @param errorMsg 错误消息
     * @param msg      请求信息
     * @return 响应数据
     */
    private Object getErrMsg(HttpServletResponse response, Throwable e, String errorMsg, String msg) {
        String exClassName = e.getClass().getName();

        /* 遍历错误信息 */
        Throwable cause = e.getCause();
        while (cause != null) {
            /* 流参数异常-不符合规定 */
            if ((cause instanceof ConstraintViolationException)) {
                exClassName = cause.getClass().getName();
                break;
            }
            cause = cause.getCause();
        }

        switch (exClassName) {
            // REST DATA 数据不存在
            case "org.springframework.data.rest.webmvc.ResourceNotFoundException":
                response.setStatus(HttpServletResponse.SC_ACCEPTED);
                return error(RepCodeEnum.ERR_NO_ID);
            // 请求方式不存在
            case "org.springframework.api.HttpRequestMethodNotSupportedException":
            case "org.springframework.web.HttpRequestMethodNotSupportedException":
                response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                return error(RepCodeEnum.UNKNOWN);
            // 参数丢失
            case "org.springframework.web.bind.MissingServletRequestParameterException":
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                return error(RepCodeEnum.ERR_PARAMS);
            // 数据保存失败
            case "org.springframework.dao.DataIntegrityViolationException":
                response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
                return error(RepCodeEnum.UNKNOWN);
            default:
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return error(RepCodeEnum.UNKNOWN);
        }
    }
}
